#!/usr/bin/env python
"""
Basic Plugin Class for Nagios Plugins, from which 'real' plugins may inherit some 
basic functionality.
"""
# last change   -> shorter 'output' message due to nodutils 255 char limits

__author__="Placi Flury grid@switch.ch"
__copyright__ = "Copyright 2010, SMSCG an AAA/SWITCH project"
__date__="25.08.2009"
__version__="0.1.2"

from os import R_OK
import os.path
from optparse import OptionParser

RETURN_CODE = {"ok":0, "warning":1, "critical":2, "unknown":3}
PROC_FILESYSTEM = "/proc"
PID_LOCATION = "/var/run"

class NagiosPluginBasic:

    def __init__(self):
        pass

    def getOptions(self):
        usage= "usage: %prog [options] \n\nDo %prog -h for more help."
        parser = OptionParser(usage=usage, version ="%prog " + __version__)

        parser.add_option("-v","--verbose", action="store",
                        dest="verbosity",
                        help="Verbosity level: 0 minimal (default), 3 maximal", default=0)

        parser.add_option("-w","--warning", action="store",
                        dest="warn_thresh", type="float",
                        help="warning threshold.")

        parser.add_option("-c","--critical", action="store",
                        dest="critical_thresh", type="float",
                        help="critical threshold.")

        parser.add_option("-H","--Hostname", action="store",
                        dest="hostname", type="string",
                        help="Hostname where plugin runs.")

        return parser

    def file_check(self,path, name, empty_check=False):
        """ checks whether file exists and is not empty 
                path    - path to file
                name    - name of file (used to print warn message)
            """

        if not os.path.exists(path):
            msg ="CRITICAL: %s not found in '%s'\n"  % (name,path)
            return msg, RETURN_CODE["critical"]

        if not os.path.isfile(path):
            msg= "CRITICAL: %s is not a file (see '%s')\n"  % (name,path)
            return msg, RETURN_CODE["critical"]

        if empty_check and (os.path.getsize(path) < 0):
            msg = "WARNING: %s in '%s' is empty\n"  % (name,path)
            return msg, RETURN_CODE["warning"]

        msg = "OK: test for %s file successful" % name
        return msg, RETURN_CODE["ok"] 



class LocalService:

    def __init__(self,name,pidloc=PID_LOCATION,procloc=PROC_FILESYSTEM):
        """
        name    - service name
        pidloc  - directory where pid file(s) is kept [default = "/var/run"]
        procloc - directory where proc files  are kept [default = "/proc"]
        """
        self.pidloc = pidloc
        self.procloc = procloc
        self.service = name
    

    def getpid(self, file_suffix=".pid"):
        """
            Reads pid from pid file. 

            return:   pid   on success
                      -1    pid file not found
                      -2    access denied 
                      -3    unknown error (exception)
            
        """
        try:
            pidfile = os.path.join(self.pidloc, self.service + file_suffix)           
            if not os.path.exists(pidfile) or not os.path.isfile(pidfile):
                return -1
            if not os.access(pidfile,R_OK):
                return -2
            fd = open(pidfile, 'r')
            pid = fd.readline()
            fd.close()
            return int(pid)
        except :
            return -3
        
    def getprocstate(self,pid):
        """
            Getting process state.
            params: pid - process id
            
            return: (see manpage of proc, "RSDZTWX")
                    R (runnig)
                    S (sleeping)
                    D (disk sleep)
                    Z (zombie)
                    T (tracing stop)
                    W (paging)
                    X (dead) or no process associated with pid
                    
                    A  access denied        
                    None (something went wrong)
        """
        
        statfile = os.path.join(self.procloc, str(pid), "stat")
        
        try:
            if not os.path.exists(statfile):
                return 'X'
            if not os.access(statfile, R_OK):
                return 'A'
            fd = open(statfile,'r')
            status = fd.readline().split(" ")[2]
            fd.close()
            return status
            
        except:
            return None
        
    def getstate(self):
        """
            returns message about service (incl. service state) and return code
        """
        
        pid = self.getpid()
        if pid == -1:
            msg  = "CRITICAL: %s is not running" % self.service
            return msg, RETURN_CODE["critical"]
        elif pid == -2:
            msg = "WARNING: access to %s pid file denied (insufficient permissions)." % self.service
            return msg,RETURN_CODE["warning"]
        elif pid == -3:
            msg = "UNKNOWN: while checking %s service." % self.service
            return msg, RETURN_CODE["unknown"]
    
        state = self.getprocstate(pid)
           
        if not state:
            msg  = "UNKNOWN: while checking %s service." % self.service 
            return msg, RETURN_CODE["unknown"]
        if state == 'A':
            msg = "WARNING: access to proc filesystem denied (insufficient permissions)."   
            return msg, RETURN_CODE["warning"]
        if state in ['X','Z']:
            msg = "CRITICAL: %s in state ('%s')." % (self.service,state)
            return msg, RETURN_CODE["critical"]
        if state in ['T','W']:
            msg = "WARNING: %s in state ('%s')." % (self.service,state)
            return msg, RETURN_CODE["warning"]
        
        msg= "OK: %s in state ('%s')." % (self.service,state)
        return msg, RETURN_CODE["ok"]
    






